﻿using System.Reflection;

namespace System.Diagnostics
{
    /// <summary>
    /// Tiny extension for diagnostics of methods based on MethodBase as simple and quick solution for flow tracking.
    /// 
    /// System.Reflection.MethodBase.GetCurrentMethod() could be used as methodBase parameter in methods.
    /// 
    /// Just add DIAGNOSTICS (should be used in conjunction with DEBUG) flag 
    /// to "Conditional compilation symbols" property in project properties TAB.
    /// </summary>
    public static class Diag
    {
        private const string ConditionValue = "DIAGNOSTICS";
        private const string LevelValue = "DIAGNOSTICS";
        private static readonly IFormatProvider FormatProvider = Globalization.CultureInfo.CurrentCulture;

        private const string StartPattern = "START";
        private const string EndPattern = "END";
        private const string TinyTemplate = "[{0:yyyy-MM-dd hh-mm-ss.ffff}][{1}] {2}";
        private const string ShortMethodBasedTemplate = "[{0:yyyy-MM-dd hh-mm-ss.ffff}][{1}] {2} - {3}";
        private const string MediumMethodBasedTemplate = "[{0:yyyy-MM-dd hh-mm-ss.ffff}][{1}] {2} - [{3}]";
        private const string LongMethodBasedTemplate = "[{0:yyyy-MM-dd hh-mm-ss.ffff}][{1}] {2} - [{3}] {4}";

        /// <summary>
        /// Gets condition (Conditional compilation symbol)
        /// </summary>
        public static string Condition
        {
            get { return ConditionValue; }
        }

        /// <summary>
        /// Outputs a block of content to the Debug Screen.
        /// </summary>
        /// <param name="methodBase"><see cref="MethodBase">MethodBase</see> parameter.</param>
        /// <param name="prefix">Prefix string to be written after normal diagnostics string pattern.</param>
        [Conditional(ConditionValue)]
        public static void WritePattern(MethodBase methodBase, string prefix)
        {
            Debug.WriteLine(MediumMethodBasedTemplate,
                            DateTime.Now, LevelValue, CreateFullPath(methodBase), prefix);
        }

        /// <summary>
        /// Outputs a block of content to the Debug Screen.
        /// </summary>
        /// <param name="methodBase"><see cref="MethodBase">MethodBase</see> parameter.</param>
        /// <param name="prefix">Prefix string to be written after normal diagnostics string pattern.</param>
        /// <param name="message">Message to be added after prefix.</param>
        [Conditional(ConditionValue)]
        public static void WritePattern(MethodBase methodBase, string prefix, string message)
        {
            Debug.WriteLine(LongMethodBasedTemplate,
                            DateTime.Now, LevelValue, CreateFullPath(methodBase), prefix, message);
        }

        /// <summary>
        /// Outputs a block of content to the Debug Screen.
        /// </summary>
        /// <param name="message">Message to be displayed.</param>
        [Conditional(ConditionValue)]
        public static void Write(string message)
        {
            Debug.WriteLine(TinyTemplate, DateTime.Now, LevelValue, message);
        }

        /// <summary>
        /// Outputs a block of content to the Debug Screen.
        /// </summary>
        /// <param name="format"><see cref="string.Format(string,object)">String format</see> template.</param>
        /// <param name="parameters">Parameters.</param>
        [Conditional(ConditionValue)]
        public static void Write(string format, params object[] parameters)
        {
            Write(string.Format(FormatProvider, format, parameters));
        }

        /// <summary>
        /// Outputs a block of content to the Debug Screen.
        /// </summary>
        /// <param name="methodBase"><see cref="MethodBase">MethodBase</see> parameter.</param>
        /// <param name="message">Message to be displayed.</param>
        [Conditional(ConditionValue)]
        public static void Write(MethodBase methodBase, string message)
        {
            Debug.WriteLine(ShortMethodBasedTemplate,
                            DateTime.Now, LevelValue, CreateFullPath(methodBase), message);
        }

        /// <summary>
        /// Outputs a block of content to the Debug Screen.
        /// </summary>
        /// <param name="methodBase"><see cref="MethodBase">MethodBase</see> parameter.</param>
        /// <param name="format"><see cref="string.Format(string,object)">String format</see> template.</param>
        /// <param name="parameters">Parameters.</param>
        [Conditional(ConditionValue)]
        public static void Write(MethodBase methodBase, string format, params object[] parameters)
        {
            Write(methodBase, string.Format(FormatProvider, format, parameters));
        }

        /// <summary>
        /// Outputs a block of content to the Debug Screen.
        /// </summary>
        /// <param name="methodBase"><see cref="MethodBase">MethodBase</see> parameter.</param>
        [Conditional(ConditionValue)]
        public static void WriteStart(MethodBase methodBase)
        {
            WritePattern(methodBase, StartPattern);
        }

        /// <summary>
        /// Outputs a block of content to the Debug Screen.
        /// </summary>
        /// <param name="methodBase"><see cref="MethodBase">MethodBase</see> parameter.</param>
        /// <param name="message">Message to be displayed.</param>
        [Conditional(ConditionValue)]
        public static void WriteStart(MethodBase methodBase, string message)
        {
            WritePattern(methodBase, StartPattern, message);
        }

        /// <summary>
        /// Outputs a block of content to the Debug Screen.
        /// </summary>
        /// <param name="methodBase"><see cref="MethodBase">MethodBase</see> parameter.</param>
        /// <param name="format"><see cref="string.Format(string,object)">String format</see> template.</param>
        /// <param name="parameters">Parameters.</param>
        [Conditional(ConditionValue)]
        public static void WriteStart(MethodBase methodBase, string format, params object[] parameters)
        {
            WriteStart(methodBase, string.Format(FormatProvider, format, parameters));
        }

        /// <summary>
        /// Outputs a block of content to the Debug Screen.
        /// </summary>
        /// <param name="methodBase"><see cref="MethodBase">MethodBase</see> parameter.</param>
        [Conditional(ConditionValue)]
        public static void WriteEnd(MethodBase methodBase)
        {
            WritePattern(methodBase, EndPattern);
        }

        /// <summary>
        /// Outputs a block of content to the Debug Screen.
        /// </summary>
        /// <param name="methodBase"><see cref="MethodBase">MethodBase</see> parameter.</param>
        /// <param name="message">Message to be displayed.</param>
        [Conditional(ConditionValue)]
        public static void WriteEnd(MethodBase methodBase, string message)
        {
            WritePattern(methodBase, EndPattern, message);
        }

        /// <summary>
        /// Outputs a block of content to the Debug Screen.
        /// </summary>
        /// <param name="methodBase"><see cref="MethodBase">MethodBase</see> parameter.</param>
        /// <param name="format"><see cref="string.Format(string,object)">String format</see> template.</param>
        /// <param name="parameters">Parameters.</param>
        [Conditional(ConditionValue)]
        public static void WriteEnd(MethodBase methodBase, string format, params object[] parameters)
        {
            WriteEnd(methodBase, string.Format(FormatProvider, format, parameters));
        }

        /// <summary>
        /// Outputs a block of content to the Debug Screen.
        /// </summary>
        /// <param name="methodBase"><see cref="MethodBase">MethodBase</see> parameter.</param>
        /// <param name="exception">Exception to be tracked.</param>
        [Conditional(ConditionValue)]
        public static void WriteError(MethodBase methodBase, Exception exception)
        {
            Debug.WriteLine("{0}{1}{2}",
                            CreateErrorMethodBasedPattern(methodBase), Environment.NewLine, exception);
        }

        /// <summary>
        /// Outputs a block of content to the Debug Screen.
        /// </summary>
        /// <param name="methodBase"><see cref="MethodBase">MethodBase</see> parameter.</param>
        /// <param name="message">Message to be displayed.</param>
        [Conditional(ConditionValue)]
        public static void WriteError(MethodBase methodBase, string message)
        {
            Debug.WriteLine("{0} {1}",
                            CreateErrorMethodBasedPattern(methodBase), message);
        }

        /// <summary>
        /// Outputs a block of content to the Debug Screen.
        /// </summary>
        /// <param name="methodBase"><see cref="MethodBase">MethodBase</see> parameter.</param>
        /// <param name="format"><see cref="string.Format(string,object)">String format</see> template.</param>
        /// <param name="args">Parameters.</param>
        [Conditional(ConditionValue)]
        public static void WriteError(MethodBase methodBase, string format, params object[] args)
        {
            WriteError(methodBase, string.Format(FormatProvider, format, args));
        }

        /// <summary>
        /// Outputs a block of content to the Debug Screen.
        /// </summary>
        /// <param name="methodBase"><see cref="MethodBase">MethodBase</see> parameter.</param>
        /// <param name="exception">Exception to be tracked.</param>
        /// <param name="message">Message to be displayed.</param>
        [Conditional(ConditionValue)]
        public static void WriteError(MethodBase methodBase, Exception exception, string message)
        {
            Debug.WriteLine("{0} {1}{2}{3}",
                            CreateErrorMethodBasedPattern(methodBase), message, Environment.NewLine, exception);
        }

        /// <summary>
        /// Outputs a block of content to the Debug Screen.
        /// </summary>
        /// <param name="methodBase"><see cref="MethodBase">MethodBase</see> parameter.</param>
        /// <param name="exception">Exception to be tracked.</param>
        /// <param name="format"><see cref="string.Format(string,object)">String format</see> template.</param>
        /// <param name="args">Parameters.</param>
        [Conditional(ConditionValue)]
        public static void WriteError(MethodBase methodBase, Exception exception, string format, params object[] args)
        {
            WriteError(methodBase, exception, string.Format(FormatProvider, format, args));
        }

        private static string CreateFullPath(MethodBase methodBase)
        {
            return methodBase != null
                       ? string.Format("{0}.{1}", methodBase.ReflectedType.FullName, methodBase.Name)
                       : "<?>";
        }

        private static string CreateErrorMethodBasedPattern(MethodBase methodBase)
        {
            return string.Format(FormatProvider,
                                 "[{0:yyyy-MM-dd hh-mm-ss.ffff}][{1}] {2} - [ERROR]",
                                 DateTime.Now, LevelValue, CreateFullPath(methodBase));
        }
    }
}
